using System;
using System.Collections.Generic;
using System.Linq;
using Ants;
using SettlersEngine;
using System.Drawing;
using System.IO;

namespace AntsBot
{

    public enum Preference
    {
        StronglyEncourage = 0,
        MildlyEncourage = 1,
        Neutral = 2,
        MildlyDiscourage = 3,
        StronglyDiscourage = 4
    }

	public class MyBot : Bot 
    {
        private HashSet<Location> Destinations;
        private Random rng;
        int turn;
        
        const int PREF_COUNT = 6;


        float[] preferenceweight = { 
                                       .01f, //strongly encourage
                                       .005f, // mildly encourage
                                       0f, // neutral
                                       -.005f, //mildly discourage
                                       -.01f  // mildly encourage
                                   };

        public MyBot()
        {
            this.Destinations = new HashSet<Location>(new LocationComparer());
            rng = new Random((int)DateTime.Now.TimeOfDay.TotalMilliseconds % 265407);
            turn = 0;
        }

        private double GetSearchRadius(GameState state)
        {
            return Math.Sqrt(state.Width * state.Height) / 15;
        }              

		public override void doTurn (GameState state)
        {
            try
            {
                if (state.TimeRemaining < 5)
                    return;

                if (turn == 0)
                {
                    Utility.Log("my team: player " + (state.MyAnts.First().Team + 1));
                    Utility.Log("map " + state.Width + "x" + state.Height);
                    Utility.Log("turn time " + state.TurnTime);
                }
                Utility.Log("");
                Utility.Log("Turn " + (turn++));
                Utility.Log("My Ants: " + state.MyAnts.Count);

                SetTurnStrategy(state);

                string stratstring = "";

                foreach (var v in Enum.GetValues(typeof(StrategyTypes)))
                {
                    stratstring += Enum.GetName(typeof(StrategyTypes), v) + ":" + Strategy.STRAT_CONSTANTS[(int)v] + ";";
                }

                Utility.Log("Strategy: " + stratstring);

                this.Destinations.Clear();
                /*
                Tile[,] wrapMap = new Tile[state.Height*3, state.Width*3];
                for (int i = 0; i < 3; i++)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        for (int row = 0; row < state.Height; row++)
                        {
                            for (int col = 0; col < state.Width; col++)
                            {
                                wrapMap[(i * state.Height) + row, (j * state.Width) + col] = state.Map[row, col];
                            }

                        }
                    }
                }
                astarwrap = new SpatialAStar<Tile, object>(wrapMap);
                 * */

                foreach (var goal in state.PersistentState.Goals)
                {
                    goal.AntExists = false; //need to check these each turn and clean up if ant gone
                }

                foreach (AntLoc ant in state.MyAnts)
                {
                    Utility.Log("ant: " + ant);
                    Goal goal = state.PersistentState.Goals.FirstOrDefault(g => g.CurrentPoint.Equals(ant));
                    

                    if (goal != null && (goal.CurrentPath.Count==0 || goal.IsTerminated(state)))
                    {
                        if (goal.CurrentPath.Count == 0)
                            Utility.Log("ant goal complete");
                        else
                            Utility.Log("ant goal terminated");
                        state.PersistentState.Goals.Remove(goal);
                        goal = null;
                    }
                    if (goal != null)
                    {
                        goal.AntExists = true;
                        Utility.Log("ant existing goal: " + String.Join(";", goal.CurrentPath.Select(p => p.Location.ToString())) + " " + Enum.GetName(typeof(StrategyTypes), goal.CurrentStrategy));
                    }
                    else
                    {
                        if (state.TimeRemaining < 50) // choose a fast strategy
                        {
                            Utility.Log("short on time (" + state.TimeRemaining + ") - choose scatter");
                            goal = new Scatter(ant).GetGoal();
                        }
                        else
                        {

                            goal = ant.GetGoal();
                            if (!goal.CurrentPath.Any())
                            {
                                Utility.Log("bad goal/path - scatter instead");
                                goal = new Scatter(ant).GetGoal();
                            }
                        }
                        state.PersistentState.Goals.Add(goal);
                        Utility.Log("new ant goal: " + String.Join(";", goal.CurrentPath.Select(p => p.Location.ToString())) + " " + Enum.GetName(typeof(StrategyTypes), goal.CurrentStrategy));

                    }


                    if (goal != null)
                    {
                        var loc = goal.CurrentPath.Dequeue();
                        var directions = ant.GetDirections(loc.Location);

                        foreach (var d in directions)
                        {
                            var newLoc = ant.GetDestination(d);
                            if (state.IsUnoccupied(newLoc) && state.IsPassable(newLoc) && !Destinations.Contains(newLoc))
                            {
                                ant.Move(d);
                                goal.CurrentPoint = newLoc;
                                goal.CurrentStep++;
                                Destinations.Add(newLoc);
                                break;
                            }
                        }
                    }
                }
                int removed = state.PersistentState.Goals.RemoveAll(g => !g.AntExists);//clean up goals for missing ants

                Utility.Log("ant goals(" + state.PersistentState.Goals.Count + ", " + removed + " removed): " + String.Join(";", state.PersistentState.Goals.Select(g => "[" + Enum.GetName(typeof(StrategyTypes), g.CurrentStrategy) + "]" + g.CurrentPoint.ToString() + "->" + g.EndPoint.ToString())));
                if (removed > 0) // losing fights - condense
                {
                    AlterStrategy(StrategyTypes.Condense, Preference.StronglyEncourage);
                    AlterStrategy(StrategyTypes.Condense, Preference.StronglyEncourage);
                    AlterStrategy(StrategyTypes.Condense, Preference.StronglyEncourage);
                }
            }
            catch (Exception exc)
            {
                Utility.Log(exc.Message);
                Utility.Log(exc.StackTrace);
            }
		}

        private void AlterStrategy(StrategyTypes strat, Preference pref)
        {
            var newval = Strategy.STRAT_CONSTANTS[(int)strat] + preferenceweight[(int)pref];
            if (newval >= 0)
            {
                Strategy.STRAT_CONSTANTS[(int)strat] = newval;
            }
            else
            {
                Strategy.STRAT_CONSTANTS[(int)strat] = 0;
            }
            /*
            var goalweight = strategies[(int)strat] + preferenceweight[(int)pref];
            if (goalweight > 0 && goalweight < 1)
            {

                float otherchange = -preferenceweight[(int)pref] / (STRAT_COUNT - 1);

                Log("strat: " + Enum.GetName(typeof(Strategy), strat) + " " + Enum.GetName(typeof(Preference), pref));


                for (int i = 0; i < STRAT_COUNT; i++)// assign the others first
                {
                    if (i != (int)strat && strategies[i] + otherchange > 0 && strategies[i] + otherchange < 1)
                    {
                        strategies[i] += otherchange;
                    }
                }

                strategies[(int)strat] += (1f - strategies.Sum());

            }
             * */
        }

        const float CLOSE_FACTOR = 2f;
        private void SetTurnStrategy(GameState state)
        {
            foreach (var a in state.MyAnts)
            {
                Utility.Log("ant loc: " + a);
            }
            Location mycenter = new Location(state,(int)Math.Round(state.MyAnts.Average(a => a.Row)), (int)Math.Round(state.MyAnts.Average(a => a.Col)));
            Utility.Log("ants center:" + mycenter);

            int totalfriends = state.MyAnts.Count;
            int closefriends = state.MyAnts.Count(a => state.GetDistance(a, mycenter) <= state.AttackRadius2 * CLOSE_FACTOR);
            Utility.Log("friends:" + totalfriends + "," + closefriends);

            int totalenemies = state.EnemyAnts.Count;
            int closeenemies = state.EnemyAnts.Count(a => state.GetDistance(a, mycenter) <= state.AttackRadius2 * CLOSE_FACTOR);
            Utility.Log("enemies:" + totalenemies + "," + closeenemies);


            int totalfood = state.FoodTiles.Count;
            int closefood = state.FoodTiles.Count(f => state.GetDistance(f, mycenter) <= state.AttackRadius2 * CLOSE_FACTOR);
            Utility.Log("food:" + totalfood + "," + closefood);


            if (turn < 50 && totalfood > 0 && totalfriends < 15)
            {
                AlterStrategy(StrategyTypes.GatherFood, Preference.StronglyEncourage);
            }

            if (closefood > 0 || totalfood > 0)
                AlterStrategy(StrategyTypes.GatherFood, Preference.MildlyEncourage);
            else
            {
                AlterStrategy(StrategyTypes.Scout, Preference.StronglyEncourage);
                AlterStrategy(StrategyTypes.Scout, Preference.StronglyEncourage);
                AlterStrategy(StrategyTypes.Scatter, Preference.StronglyEncourage);
                AlterStrategy(StrategyTypes.SpreadOut, Preference.StronglyEncourage);
                AlterStrategy(StrategyTypes.SpreadOut, Preference.StronglyEncourage);
            }

            if (closeenemies > closefriends)
            {
                AlterStrategy(StrategyTypes.Fight, Preference.StronglyDiscourage);
                AlterStrategy(StrategyTypes.Condense, Preference.MildlyEncourage);
                AlterStrategy(StrategyTypes.Scatter, Preference.MildlyDiscourage);
                AlterStrategy(StrategyTypes.Retreat, Preference.StronglyEncourage);
            }
            else if (closeenemies == closefriends && closeenemies > 0)
            {
                AlterStrategy(StrategyTypes.Condense, Preference.StronglyEncourage);
                AlterStrategy(StrategyTypes.Scatter, Preference.StronglyDiscourage);
                AlterStrategy(StrategyTypes.Fight, Preference.MildlyEncourage);
            }
            else  //if (closeenemies < closefriends)
            {
                if (closeenemies > 0)
                {
                    AlterStrategy(StrategyTypes.Fight, Preference.StronglyEncourage);
                    AlterStrategy(StrategyTypes.Retreat, Preference.StronglyDiscourage);
                    AlterStrategy(StrategyTypes.Condense, Preference.MildlyEncourage);
                    AlterStrategy(StrategyTypes.Scatter, Preference.StronglyDiscourage);
                }
                else
                {
                    AlterStrategy(StrategyTypes.Scout, Preference.MildlyEncourage);
                    AlterStrategy(StrategyTypes.Condense, Preference.MildlyDiscourage);
                    AlterStrategy(StrategyTypes.Scatter, Preference.MildlyEncourage);
                }
            }

            if (totalfriends < totalenemies && totalenemies > 0)
            {
                if (closefood > closeenemies)
                {
                    AlterStrategy(StrategyTypes.GatherFood, Preference.StronglyEncourage);
                    AlterStrategy(StrategyTypes.Fight, Preference.MildlyDiscourage);
                    AlterStrategy(StrategyTypes.Retreat, Preference.MildlyDiscourage);
                    AlterStrategy(StrategyTypes.Scatter, Preference.MildlyEncourage);
                }
                else
                {
                    AlterStrategy(StrategyTypes.Scout, Preference.StronglyEncourage);
                    AlterStrategy(StrategyTypes.Fight, Preference.MildlyDiscourage);
                    AlterStrategy(StrategyTypes.Scatter, Preference.MildlyEncourage);
                }
            }
            else  //if (totalfriends > totalenemies)
            {
                if (totalenemies > 0)
                {
                    AlterStrategy(StrategyTypes.Fight, Preference.StronglyEncourage);
                }
                AlterStrategy(StrategyTypes.Scout, Preference.MildlyEncourage);
                AlterStrategy(StrategyTypes.Scatter, Preference.MildlyEncourage);
            }

        }

		public static void Main (string[] args) {
			new Ants.Ants().playGame(new MyBot());
		}

	}
}